﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace gemGenerator
{
    static class EdgeRounder
    {
        /// <summary>
        /// adds a vertex that lies on the edge of the gem and touches the girdle
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="iv1">edge end 1</param>
        /// <param name="iv2">edge end 2</param>
        /// <param name="transform">used polar transformation</param>
        /// <param name="r">radius of the girdle</param>
        /// <param name="RoundedGem">list to put the new vertex into</param>
        static void AddEdgeMidpoint(IList<VectorD> vertices, int iv1, int iv2, CoordinateTransform transform, double r, IList<VectorD> RoundedGem)
        {//halving intevals
            double epsilon = 1e-4;
            var x0 = vertices[iv1];
            var x1 = vertices[iv2];
            double r0 = transform.Inverse(x0).X,
                   r1 = transform.Inverse(x1).X;
            if( (r>r0&& r>r1) || (r<r0&& r<r1)) return; //r is out of range
            if(r0>r1)
            {
                var b = x0; x0 = x1; x1 = b;
            }
            int iter = 0;
            while (iter<15)
            {
                var x2 = 0.5 * (x0 + x1);
                double val = transform.Inverse(x2).X;
                if(Math.Abs(val-r)<epsilon)
                {
                    RoundedGem.Add(x2); 
                    return;
                }
                if (val > r)
                    x1 = x2;
                else
                    x0 = x2;
                ++iter;
            }
            
        }
        /// <summary>
        /// creates gem with a girdle
        /// </summary>
        /// <param name="vertices">vertices of the original gem</param>
        /// <param name="faces">faces of the original gem</param>
        /// <param name="transform">used polar transform</param>
        /// <param name="r">radius of the girdle</param>
        /// <param name="num">number of rounding lines</param>
        /// <returns>vertices of the girdled gem</returns>
        public static IList<VectorD> MakeGirdle(IList<VectorD> vertices, LinkedList<Triangle> faces, 
            CoordinateTransform transform, double r, int num)
        {
            HashSet<int> Horns = new HashSet<int>();
            List<VectorD> RoundedGem = new List<VectorD>();
            //
            for (int v = 0; v < vertices.Count; ++v)
            {
                if (transform.Inverse(vertices[v]).X > r)
                    Horns.Add(v);
                else RoundedGem.Add(vertices[v]);
            }
            //trojuhelniky kterych se to tyka
            LinkedList<VectorD> rods = new LinkedList<VectorD>();
            
            for(int i = 0; i< num; ++i)
            {
                rods.AddFirst(transform.Transform(new VectorD(r, (((double)i) / num) * (2 * Math.PI), 0)));
            }
            foreach (Triangle t in faces)
            {
                bool h1 = Horns.Contains(t.V1),
                     h2 = Horns.Contains(t.V2),
                     h3 = Horns.Contains(t.V3);
                if (h1 || h2 || h3)
                {
                   
                     if (h1)
                    {
                        if (!h2)
                            AddEdgeMidpoint(vertices, t.V1, t.V2, transform, r, RoundedGem);
                        if (!h3)
                            AddEdgeMidpoint(vertices, t.V1, t.V3, transform, r, RoundedGem);
                    }
                    if(h2)
                    {
                        if (!h1)
                            AddEdgeMidpoint(vertices, t.V1, t.V2, transform, r, RoundedGem);
                        if (!h3)
                            AddEdgeMidpoint(vertices, t.V2, t.V3, transform, r, RoundedGem);
                    }

                    if (h3)
                    {
                        if (!h1)
                            AddEdgeMidpoint(vertices, t.V1, t.V3, transform, r, RoundedGem);
                        if (!h2)
                            AddEdgeMidpoint(vertices, t.V2, t.V3, transform, r, RoundedGem);
                    }
                    
                    foreach (VectorD rod in rods)
                    {
                        VectorD insec = Intersection(vertices, t, rod);
                        VectorD tr;
                        if (insec != null) tr= transform.Inverse(insec);
                        VectorD lol = transform.Inverse(rod);
                        if (insec != null)
                            RoundedGem.Add(insec);
                    }
                }
            }
            // vrat nedotknute vertexy + pruseciky
                return RoundedGem;
        }
        
        /// <summary>
        /// intersection of triangle and a line perpendicular to XY plane
        /// </summary>
        /// <param name="vertices"></param>
        /// <param name="face">intersected triangle</param>
        /// <param name="rod">intersecting line</param>
        /// <returns>intersection</returns>
        static VectorD Intersection(IList<VectorD> vertices, Triangle face, VectorD rod)
        {
            VectorD o = vertices[face.V1];
            VectorD b1 = vertices[face.V2] - o,
                    b2 = vertices[face.V3] - o;
            VectorD r = rod - o;

            double det = b1.X * b2.Y - b2.X * b1.Y;
            double t1 = (r.X * b2.Y - b2.X * r.Y)/det;
            double t2 = (b1.X * r.Y - r.X * b1.Y)/det;

            const double epsilon = 1e-5;
            if (t1 < -epsilon || t2 < -epsilon || t1 + t2 > 1 + epsilon) return null;
            VectorD ret = new VectorD(rod.X, rod.Y,(o.Z + t1 * b1.Z + t2 * b2.Z));

            return ret;

        }
    }
}
